package com.faneui.transfer.common.encrypt;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;


public abstract class RSAUtils {
	
	
	public static final String  signingAlgorithm = "SHA1WithRSA";
	
	public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; // 加密block需要预留11字节
	
	public static final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVrWtXSL9zVYb24dc1wlo+BUrodvuwm/jr+Bj2"+
			"TsxuMCfG6UyoIZ6hn28mEltvjhGXnkNLQLELZNzMf6nAtkOElOTpTH/nV2aO2lPdYvb9+9Ri/PV5"+
			"344qw6Piw4lLL2izeqxpyLZJo+bmPR/ZLCjrJYCDKrfoEKQPHhunsvDtcQIDAQAB";
	
	public static final String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJWta1dIv3NVhvbh1zXCWj4FSuh2"+
			"+7Cb+Ov4GPZOzG4wJ8bpTKghnqGfbyYSW2+OEZeeQ0tAsQtk3Mx/qcC2Q4SU5OlMf+dXZo7aU91i"+
			"9v371GL89XnfjirDo+LDiUsvaLN6rGnItkmj5uY9H9ksKOslgIMqt+gQpA8eG6ey8O1xAgMBAAEC"+
			"gYAKthhibLBWvp4j+NF/5WKz4YkUzb8BSogryttudQEoOYX/NB7qQU2q31AC93lXqGEV6puCPSTu"+
			"Xc93SwOI6QI31Eg7cP4R1Td6Tc9JYLKLFlm/LSAS3yaLRKvIB+sKiswRx6LJ8EKyQVm+ey0GzxAI"+
			"RSgiHn08d92mFqshIy+8AQJBAOitumXLT+38yxpRpL9roiPlV/sEVt7qz4hcI/lnB0zWDBVAhnwo"+
			"5QL+T5jOfw5Rd7cMcxa4+f9EotffM9VaCZECQQCkrfk7jlZi5pICE/NtSr4nzK5GpltEPsoUk8wf"+
			"4bi0QIcZMNnab8fAPWvoI2SXhLzWQCNd8CPT5474/NFn27XhAkASWDgc9HWdztN4hL1XdjX3XIqe"+
			"OM5cjujD2HfhnvTdYvba6hZwTY0YulIuOwGbcGLe5LViMj1Lme641Fky1gFxAkAgwvOoY4LQHirB"+
			"+xIclhgKdu/c4k+Z3hv5yAV9+jvysDuwSUWt7TsCBbYn8pKaii+/N/dRQur9xz1sT2+hh9ZBAkEA"+
			"ldYqdv3K37ddz5K9XRC2d+ddBl7eLlxSxbfnt7q/a/6Oc5+IsRgsO0nATrCrAaOmqueTEOnx7ArP"+
			"WyF2mBYV3w==";
	
	public static final String KEY_ALGORITHM = "RSA";
	private static final String PUBLIC_KEY = "RSAPublicKey";
	private static final String PRIVATE_KEY = "RSAPrivateKey";

	/**
	 * 得到公钥字符串
	 * @param keyMap
	 * @return
	 * @throws Exception
	 */
	public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PUBLIC_KEY);
		byte[] publicKey = key.getEncoded();
		return encryptBASE64(key.getEncoded());
	}
	
	/**
	 * 得到私钥字符串
	 * @param keyMap
	 * @return
	 * @throws Exception
	 */
	public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PRIVATE_KEY);
		byte[] privateKey = key.getEncoded();
		return encryptBASE64(key.getEncoded());
	}

	public static byte[] decryptBASE64(String key) throws Exception {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

	/**
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptBASE64(byte[] key) throws Exception {
		return (new BASE64Encoder()).encodeBuffer(key);
	}

	/**
	 * 得到公私钥
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> initKey() throws Exception {
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGen.initialize(1024);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		Map<String, Object> keyMap = new HashMap<String, Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}
	
	/**
	 * 从字符串中加载私钥
	 * 
	 * @param privateKeyStr 公钥数据字符串
	 *           
	 * @throws Exception 加载私钥时产生的异常
	 *             
	 */
	public static RSAPrivateKey getPrivateKey(String privateKeyStr) throws Exception {
		try { 
			byte[] buffer = Base64.decodeBase64(privateKeyStr);
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory
					.generatePrivate(keySpec);
			return privateKey;
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("私钥非法"); 
		} catch (NullPointerException e) {
			throw new Exception("私钥数据为空");
		}
	}

	
	/**
	 * 从字符串中加载公钥
	 * 
	 * @param publicKeyStr
	 *            公钥数据字符串
	 * @throws Exception
	 *             加载公钥时产生的异常
	 */
	public static RSAPublicKey getPublicKey(String publicKeyStr) throws Exception {
		try { 
			byte[] buffer = Base64.decodeBase64(publicKeyStr);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
			RSAPublicKey publicKey = (RSAPublicKey) keyFactory
					.generatePublic(keySpec);
			return publicKey;
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("公钥非法"); 
		} catch (NullPointerException e) {
			throw new Exception("公钥数据为空");
		}
	}
	
	/**
     *  私钥加签
     * @param prvKeyIn
     * @param str
     * @return
     * @throws Exception
     */
    public byte[] rsaPriSign(RSAPrivateKey prvKeyIn,String str) throws Exception{
    	Signature sign = Signature.getInstance("SHA1WithRSA");	
		sign.initSign(prvKeyIn);
		sign.update(str.getBytes("UTF-8"));
		return sign.sign();
    }
    
    /**
     *  公钥验签
     * @param prvKeyIn
     * @param str
     * @return
     * @throws Exception
     */
    public static boolean rsaPubVerify(RSAPublicKey peerPubKey,String unsign,
    		byte[] signd,String charset) throws Exception{
    	boolean isValid = false;
		String SIGNATURE_ALGORITHM = "SHA1withRSA";
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initVerify(peerPubKey);
		
		signature.update(unsign.getBytes(charset)); 
		isValid = signature.verify(signd);
		return isValid;
    }

	/**
	 * RAS加密
	 * 
	 * @param peerPubKey
	 *            公钥
	 * @param data
	 *            待加密信息
	 * @return byte[]
	 * @throws Exception
	 */
	public static byte[] rsaPubEncrypt(RSAPublicKey publicKey,byte[] plainBytes, String charset)
			throws Exception {
		String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; // 加密block需要预留11字节
		int KEYBIT = 2048;
		int RESERVEBYTES = 11;
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		int decryptBlock = KEYBIT / 8; // 256 bytes
		int encryptBlock = decryptBlock - RESERVEBYTES; // 245 bytes
		// 计算分段加密的block数 (向上取整)
		int nBlock = (plainBytes.length / encryptBlock);
		if ((plainBytes.length % encryptBlock) != 0) { // 余数非0，block数再加1
			nBlock += 1;
		}
		// 输出buffer, 大小为nBlock个decryptBlock
		ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock
				* decryptBlock);
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		// cryptedBase64Str =
		// Base64.encodeBase64String(cipher.doFinal(plaintext.getBytes()));
		// 分段加密
		for (int offset = 0; offset < plainBytes.length; offset += encryptBlock) {
			// block大小: encryptBlock 或 剩余字节数
			int inputLen = (plainBytes.length - offset);
			if (inputLen > encryptBlock) {
				inputLen = encryptBlock;
			}
			// 得到分段加密结果
			byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
			// 追加结果到输出buffer中
			outbuf.write(encryptedBlock);
		}
		return outbuf.toByteArray();
	}


	/**
	 * RSA解密
	 * 
	 * @param localPrivKey
	 *            私钥
	 * @param cryptedBytes
	 *            待解密信息
	 * @return byte[]
	 * @throws Exception
	 */
	public static byte[] rsaPriDecrypt(RSAPrivateKey privateKey, byte[] cryptedBytes, 
			String charset) throws Exception { 
		byte[] data = null;

		// 如果是Base64编码的话，则要Base64解码
		data = cryptedBytes;

		int KEYBIT = 2048;
		int RESERVEBYTES = 11;
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		int decryptBlock = KEYBIT / 8; // 256 bytes
		int encryptBlock = decryptBlock - RESERVEBYTES; // 245 bytes
		// 计算分段解密的block数 (理论上应该能整除)
		int nBlock = (data.length / decryptBlock);
		// 输出buffer, , 大小为nBlock个encryptBlock
		ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock
				* encryptBlock);
		cipher.init(Cipher.DECRYPT_MODE, privateKey); 
		// 分段解密
		for (int offset = 0; offset < data.length; offset += decryptBlock) {
			// block大小: decryptBlock 或 剩余字节数
			int inputLen = (data.length - offset);
			if (inputLen > decryptBlock) {
				inputLen = decryptBlock;
			}

			// 得到分段解密结果
			byte[] decryptedBlock = cipher.doFinal(data, offset, inputLen);
			// 追加结果到输出buffer中
			outbuf.write(decryptedBlock);
		}
		outbuf.flush();
		outbuf.close();
		return outbuf.toByteArray();
	}
	
	/**
	 *  根据明文加密得到密文
	 * @param str
	 * @param charset
	 * @return
	 * @throws Exception
	 */
	public static String rsaEncrypt(String str,String charset) throws Exception{
		RSAPublicKey pubKey = getPublicKey(publicKey);
		byte[] signedAttributes = rsaPubEncrypt(pubKey,str.getBytes(charset),charset) ;
		return Base64.encodeBase64String(signedAttributes) ;
	}
	
	/**
	 * 验证明文密码是否正确
	 * @param loginPwd 前台输入明文密码
	 * @param basePwd  数据库存储密码
	 * @throws Exception 
	 */
	public static boolean checkPwdCorrect(String loginPwd,String base64Pwd,String charset) throws Exception{
		RSAPublicKey pubKey = getPublicKey(publicKey);
		RSAPrivateKey priKey = getPrivateKey(privateKey);
		byte[] signedAttributes = rsaPubEncrypt(pubKey, loginPwd.getBytes(charset), charset) ;
		String str1 = new String(rsaPriDecrypt(priKey,signedAttributes, charset) ,charset);
		String str2 = new String(rsaPriDecrypt(priKey,Base64.decodeBase64(base64Pwd) ,charset ) ,charset);
		return str1.equals(str2);
	}
	
	/**
	 * 验证密文密码是否正确
	 * @param base64LoginPwd 前台输入密文密码
	 * @param base64Pwd 输出库存储密文密码
	 * @throws Exception 
	 * @throws UnsupportedEncodingException 
	 */
	public static boolean  checkEncryptPwdCorrect(String base64LoginPwd,String base64Pwd,String charset) throws UnsupportedEncodingException, Exception{
		RSAPrivateKey priKey = getPrivateKey(privateKey);
		String str1 = new String(rsaPriDecrypt(priKey,Base64.decodeBase64(base64LoginPwd) , charset) ,charset);
		String str2 = new String(rsaPriDecrypt(priKey,Base64.decodeBase64(base64Pwd) ,charset ) ,charset);
		return str1.equals(str2);
	}
	
	/**
	 * 验证明文、密文密码是否正确例子
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		String loginPwd = "faneui";
		String base64LoginPwd = "Wo/RxTC7jfGSXtQBClUs8rpqHuzII892wU2F0zNmTtJ6tS7p1cNlmkMcf/VzzBGnc5IuS+UkSWm9anrOq6p1EjlRvh98AS3X2fr8EOMd93GdtEiHIoztxxx93jfZxWLGO5ztxJQxM+85aebvWzIF74S3aW8oAdnF0rR/Yxc9EQg=";
		String base64Pwd = "Z+7UDnQhnASx7crQqAvtBjyYpvbCmAxFk528R9rA7MOk7WmxulKUWM9PwKZoXvBg3OICo576r3qXDPltzCBmz8jxBt1uijGOu8IQS9bxdfZjcPoqKBc337mvOI/OjV2PL5mrdV3ccZLrLjxQ8N16PeD+nhwSZi/iIZNwg5rD4OY=";
		String charset = "utf-8";
		System.out.println(checkPwdCorrect(loginPwd, base64Pwd, charset));
		System.out.println(checkEncryptPwdCorrect(base64LoginPwd, base64Pwd, charset));
	}
	
	/**
	 *  使用公私钥进行加密的例子
	 * @param args
	 * @throws Exception
	 */
	public static void main1(String[] args) throws Exception {
		String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVrWtXSL9zVYb24dc1wlo+BUrodvuwm/jr+Bj2"+
		"TsxuMCfG6UyoIZ6hn28mEltvjhGXnkNLQLELZNzMf6nAtkOElOTpTH/nV2aO2lPdYvb9+9Ri/PV5"+
		"344qw6Piw4lLL2izeqxpyLZJo+bmPR/ZLCjrJYCDKrfoEKQPHhunsvDtcQIDAQAB";

		String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJWta1dIv3NVhvbh1zXCWj4FSuh2"+
		"+7Cb+Ov4GPZOzG4wJ8bpTKghnqGfbyYSW2+OEZeeQ0tAsQtk3Mx/qcC2Q4SU5OlMf+dXZo7aU91i"+
		"9v371GL89XnfjirDo+LDiUsvaLN6rGnItkmj5uY9H9ksKOslgIMqt+gQpA8eG6ey8O1xAgMBAAEC"+
		"gYAKthhibLBWvp4j+NF/5WKz4YkUzb8BSogryttudQEoOYX/NB7qQU2q31AC93lXqGEV6puCPSTu"+
		"Xc93SwOI6QI31Eg7cP4R1Td6Tc9JYLKLFlm/LSAS3yaLRKvIB+sKiswRx6LJ8EKyQVm+ey0GzxAI"+
		"RSgiHn08d92mFqshIy+8AQJBAOitumXLT+38yxpRpL9roiPlV/sEVt7qz4hcI/lnB0zWDBVAhnwo"+
		"5QL+T5jOfw5Rd7cMcxa4+f9EotffM9VaCZECQQCkrfk7jlZi5pICE/NtSr4nzK5GpltEPsoUk8wf"+
		"4bi0QIcZMNnab8fAPWvoI2SXhLzWQCNd8CPT5474/NFn27XhAkASWDgc9HWdztN4hL1XdjX3XIqe"+
		"OM5cjujD2HfhnvTdYvba6hZwTY0YulIuOwGbcGLe5LViMj1Lme641Fky1gFxAkAgwvOoY4LQHirB"+
		"+xIclhgKdu/c4k+Z3hv5yAV9+jvysDuwSUWt7TsCBbYn8pKaii+/N/dRQur9xz1sT2+hh9ZBAkEA"+
		"ldYqdv3K37ddz5K9XRC2d+ddBl7eLlxSxbfnt7q/a/6Oc5+IsRgsO0nATrCrAaOmqueTEOnx7ArP"+
		"WyF2mBYV3w==";
		
		String str = "faneui";
		
		RSAPublicKey pubKey = getPublicKey(publicKey);
		
		RSAPrivateKey priKey = getPrivateKey(privateKey);
		System.out.println(System.currentTimeMillis());
		byte[] signedAttributes = rsaPubEncrypt(pubKey, str.getBytes("UTF-8"), "UTF-8") ; 
		String baseStr= Base64.encodeBase64String(signedAttributes) ;
		System.out.println(System.currentTimeMillis());
		String str1 = new String(rsaPriDecrypt(priKey,Base64.decodeBase64(baseStr) , "utf-8") ,"utf-8");
		System.out.println(System.currentTimeMillis());
		System.out.println(str1);
	}
	
	/**
	 *  生产公私钥例子
	 * @param args
	 */
	public static void main3(String[] args) {
		Map<String, Object> keyMap;
		try {
			keyMap = initKey();
			String publicKey = getPublicKey(keyMap);
			System.out.println(publicKey);
			String privateKey = getPrivateKey(keyMap);
			System.out.println(privateKey);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}